package com.membership.cloud.service;

import com.membership.cloud.exception.EmailExistException;
import com.membership.cloud.exception.InvalidBarcodeException;
import com.membership.cloud.exception.InvalidEmailException;
import com.membership.cloud.exception.InvalidItemException;
import com.membership.cloud.exception.InvalidItemException;
import com.membership.cloud.exception.NoInstanceException;
import com.membership.cloud.exception.PhoneExistException;
import com.membership.cloud.exception.ServerTechnicalException;
import com.membership.cloud.jpa.domain.Customer;
import com.membership.cloud.jpa.repository.CustomerRepository;
import com.membership.cloud.web.model.CustomerPageResponse;
import com.membership.cloud.web.model.CustomerResponse;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.time.DateUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.stereotype.Service;

import javax.transaction.Transactional;
import java.sql.Timestamp;
import java.text.ParseException;
import java.util.Date;

/**
 * Created by hiephn on 2014/07/21.
 */
@Service
public class CustomerService extends AbstractService {

    @Autowired
    private CustomerRepository customerRepository;

    /**
     * Filter customer list by their attributes.
     * Any of customer attributes contains filterKey will be returned.
     *
     * @param page
     * @param pageSize
     * @param filterKey
     * @param birthdayString pattern: yyyy/MM/dd
     * @return
     * @throws ServerTechnicalException
     * @throws com.membership.cloud.exception.InvalidItemException
     */
    public CustomerPageResponse filter(int page, int pageSize, String filterKey,
            String birthdayString) throws ServerTechnicalException, InvalidItemException {
        PageRequest pageRequest = new PageRequest(page, pageSize);
        Page<Customer> customerPage;
        if (StringUtils.isEmpty(filterKey)) { // get all
            customerPage = customerRepository.findAll(pageRequest);
        } else { // filter result
            filterKey = this.wildcardSearchKey(filterKey);
            if (StringUtils.isEmpty(birthdayString)) { // filter without birthday
                customerPage = customerRepository.
                    filterByStringAttributesOrderByNameAsc(
                        filterKey, pageRequest);
            } else { // filter with birthday
                Timestamp birthday = this.toBirthday(birthdayString);
                customerPage = customerRepository.
                        filterByStringAttributesOrBirthDayOrderByNameAsc(
                                filterKey, birthday, pageRequest);
            }
        }
        CustomerPageResponse customerPageResponse = new CustomerPageResponse();
        customerPageResponse.from(customerPage, CustomerResponse.class);
        return customerPageResponse;
    }

    /**
     * Get customer list by their attributes.
     * Any not null/empty attribute must be satisfied.
     *
     * @param page
     * @param pageSize
     * @param name
     * @param fullName
     * @param birthdayString pattern: yyyy/MM/dd
     * @param barcode
     * @param phone
     * @return
     * @throws ServerTechnicalException
     */
    public CustomerPageResponse get(int page, int pageSize, String name,
            String fullName, String birthdayString, String barcode,
            String phone, String email) throws ServerTechnicalException, InvalidItemException {
        PageRequest pageRequest = new PageRequest(page, pageSize);
        Page<Customer> customerPage;
        if (StringUtils.isEmpty(birthdayString)) {
            customerPage = customerRepository.
                getByStringAttributesOrderByNameAsc(
                    this.wildcardSearchKey(name),
                    this.wildcardSearchKey(fullName),
                    this.wildcardSearchKey(barcode),
                    this.wildcardSearchKey(phone),
                    this.wildcardSearchKey(email),
                    pageRequest);
        } else {
            Timestamp birthday = this.toBirthday(birthdayString);
            customerPage = customerRepository.
                    getByStringAttributesAndBirthDayOrderByNameAsc(
                            this.wildcardSearchKey(name),
                            this.wildcardSearchKey(fullName),
                            this.wildcardSearchKey(barcode),
                            this.wildcardSearchKey(phone),
                            this.wildcardSearchKey(email),
                            birthday,
                            pageRequest);
        }
        CustomerPageResponse customerPageResponse = new CustomerPageResponse();
        customerPageResponse.from(customerPage, CustomerResponse.class);
        return customerPageResponse;
    }

    @Transactional
    public CustomerResponse create(String name, String fullName,
            String birthdayString, String barcode, String phone, String email)
            throws InvalidItemException, EmailExistException, PhoneExistException {
        // Check email
        this.validateEmailFormat(email);
        if (customerRepository.findByEmail(email) > 0) {
            throw new EmailExistException(email + " already exists");
        }
        // Check phone
        String standardPhone = this.standardizePhone(phone);
        if (customerRepository.findByPhone(standardPhone) > 0) {
            throw new PhoneExistException(standardPhone + " already exists");
        }
        // Check birthday
        Timestamp birthday = this.toBirthday(birthdayString);
        // Check barcode
        this.validateBarcode(barcode);

        Customer customer = new Customer();
        customer.setName(name);
        customer.setPhone(standardPhone);
        customer.setBarcode(barcode);
        customer.setFullName(fullName);
        customer.setBirthday(birthday);
        customer.setEmail(email);

        CustomerResponse customerResponse = new CustomerResponse(customer);
        return customerResponse;
    }

    @Transactional
    public CustomerResponse update(int id, String name, String fullName,
            String birthdayString, String barcode, String phone, String email)
            throws NoInstanceException, InvalidItemException, EmailExistException,
            PhoneExistException {
        Customer customer = customerRepository.findOne(id);
        if (customer == null) {
            throw new NoInstanceException("Customer", id);
        }
        // Check email
        if (!StringUtils.equals(email, customer.getEmail())) {
            this.validateEmailFormat(email);
            if (customerRepository.findByEmail(email) > 0) {
                throw new EmailExistException(email + " already exists");
            }
            customer.setEmail(email);
        }
        // Check phone
        String standardPhone = this.standardizePhone(phone);
        if (!StringUtils.equals(standardPhone, customer.getPhone())) {
            if (customerRepository.findByPhone(standardPhone) > 0) {
                throw new PhoneExistException(standardPhone + " already exists");
            }
            customer.setPhone(standardPhone);
        }
        // Check birthday
        Timestamp birthday = this.toBirthday(birthdayString);
        // Check barcode
        this.validateBarcode(barcode);

        customer.setBirthday(birthday);
        customer.setBarcode(barcode);
        customer.setFullName(fullName);
        customer.setName(name);

        CustomerResponse customerResponse = new CustomerResponse(customer);
        return customerResponse;
    }

    @Transactional
    public void delete(int id) {
        customerRepository.delete(id);
    }

    public void validateBarcode(String barcode) throws InvalidBarcodeException {
        // TODO: barcode pattern validation
    }

    protected Timestamp toBirthday(String birthdayString)
            throws InvalidItemException {
        final String BIRTHDAY_PATTERN = "yyyy/MM/dd";
        try {
            Date date = DateUtils.parseDate(birthdayString, new String[]{BIRTHDAY_PATTERN});
            Timestamp birthDay = new Timestamp(date.getTime());
            return birthDay;
        } catch (ParseException e) {
            throw new InvalidItemException(
                    "Invalid birthday. Expected: " + BIRTHDAY_PATTERN);
        }
    }
}
